/*
 * libFuse.c
 *
 *  Created on: 18/04/2012
 *      Author: utnso
 */

#include "libFuse.h"

//VARIABLES GLOBALES A ESTE ARCHIVO
static struct fuse_operations ASLPC_operations = { .getattr = &ASLPC_getattr, .mkdir = &ASLPC_mkdir, .rmdir = &ASLPC_rmdir,
//.rename = &ASLPC_rename,
		.ftruncate = &ASLPC_ftruncate, .open = &ASLPC_open, .read = &ASLPC_read, .write = &ASLPC_write,
		//.flush = &ASLPC_flush,
		.release = &ASLPC_release, .readdir = &ASLPC_readdir,
		//.access = &ASLPC_access,
		.create = &ASLPC_create,
//.unlink = &ASLPC_unlink
		};

void iniciarFUSE(void* argFUSE) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se iniciara FUSE.", NIVEL_LOG_BAJO);
	argFUSE_t* arg = (argFUSE_t*) argFUSE;
	*((*arg).res) = fuse_main((*arg).argc, (*arg).argv, &ASLPC_operations, NULL);
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Fin de ejecucion de FUSE.", NIVEL_LOG_BAJO);
}

int32_t ASLPC_getattr(const char* path, struct stat* stbuf) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_getattr.", NIVEL_LOG_BAJO);

	//TODO CONSULTAR A LA CACHE

	//ARMO EL PAQUETE A ENVIAR
	paquete_t paquete;
	argGetAttr_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	paquete.tipo = TIPO_OBTENER_ATRIBUTOS;
	paquete.pLength = tamanioEnvioObtenerAtributos(arg);
	paquete.payload = desparsearEnvioObtenerAtributos(arg);
	char* envio = serializar(paquete);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		reciboConAtributos_t reciboConAtributos = parsearReciboConAtributos(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
		stbuf->st_mode = reciboConAtributos.mode;
		stbuf->st_size = reciboConAtributos.size;
		res = reciboConAtributos.resultado;
	} else {
		res = -ENOENT;
	}
	free(recibo);

	return res;
}

int32_t ASLPC_mkdir(const char* path, mode_t modo) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_mkdir.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argCrearDir_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	paqueteEnvio.tipo = TIPO_CREAR_DIRECTORIO;
	paqueteEnvio.pLength = tamanioEnvioCrearDirectorio(arg);
	paqueteEnvio.payload = desparsearEnvioCrearDirectorio(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

int32_t ASLPC_rmdir(const char* path) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_rmdir.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argBorrarDir_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	paqueteEnvio.tipo = TIPO_BORRAR_DIRECTORIO;
	paqueteEnvio.pLength = tamanioEnvioBorrarDirectorio(arg);
	paqueteEnvio.payload = desparsearEnvioBorrarDirectorio(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

/*int32_t ASLPC_rename(const char* pathViejo, const char* pathNuevo){
 loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_rename.", NIVEL_LOG_BAJO);
 //ARMO EL PAQUETE A ENVIAR
 paquete_t paqueteEnvio;
 argRenombrar_t arg;
 arg.pathViejo = (char*) malloc(strlen(pathViejo) + 1);
 memcpy(arg.pathViejo, pathViejo, strlen(pathViejo) + 1);
 arg.pathNuevo = (char*) malloc(strlen(pathNuevo) + 1);
 memcpy(arg.pathNuevo, pathNuevo, strlen(pathNuevo) + 1);
 paqueteEnvio.tipo = TIPO_RENOMBRAR;
 paqueteEnvio.pLength = tamanioEnvioRenombrar(arg);
 paqueteEnvio.payload = desparsearEnvioRenombrar(arg);
 char* envio = serializar(paqueteEnvio);
 free(arg.pathViejo);
 free(arg.pathNuevo);

 char* recibo;

 //HAGO EL PEDIDO
 uint8_t error = unPedidoUnRecibo(envio, &recibo);
 free(envio);
 free(paqueteEnvio.payload);

 int32_t res;
 if(error != 0){
 paquete_t paqueteRecibo = deserializar(recibo);
 res = parsearReciboSinBuffer(paqueteRecibo.payload);
 free(paqueteRecibo.payload);
 }else{
 res = -ENOENT;
 }
 free(recibo);

 return res;
 }*/

int32_t ASLPC_ftruncate(const char * path, off_t tamanio, struct fuse_file_info * info) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_ftruncate.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argTruncarArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	arg.offset = tamanio;
	//arg.fInfo = info;
	paqueteEnvio.tipo = TIPO_TRUNCAR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioTruncarArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioTruncarArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}
	free(recibo);

	return res;
}

int32_t ASLPC_open(const char* path, struct fuse_file_info* fi) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_open.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argAbrirArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	//arg.fInfo = fi;
	paqueteEnvio.tipo = TIPO_ABRIR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioAbrirArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioAbrirArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

int32_t ASLPC_read(const char* path, char* buf, size_t size, off_t offset, struct fuse_file_info* fi) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_read.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argLeerArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	arg.size = size;
	arg.offset = offset;
	//arg.fInfo = fi;
	paqueteEnvio.tipo = TIPO_LEER_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioLeerArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioLeerArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		reciboConBuffer_t reciboConBuffer = parsearReciboConBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);

		memcpy(buf, reciboConBuffer.buffer, reciboConBuffer.tamanio);
		free(reciboConBuffer.buffer);
		res = reciboConBuffer.resultado;
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

int32_t ASLPC_write(const char* path, const char* buf, size_t size, off_t offset, struct fuse_file_info* fi) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_write.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argEscribirArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	arg.buffer = (char*) malloc(size);
	memcpy(arg.buffer, buf, size);
	arg.size = size;
	arg.offset = offset;
	//arg.fInfo = fi;
	paqueteEnvio.tipo = TIPO_ESCRIBIR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioEscribirArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioEscribirArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);
	free(arg.buffer);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

/*int32_t ASLPC_flush(const char* path, struct fuse_file_info* info){
 return 0;
 }*/

int32_t ASLPC_release(const char* path, struct fuse_file_info* info) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_release.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argCerrarArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	//arg.fInfo = info;
	paqueteEnvio.tipo = TIPO_CERRAR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioCerrarArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioCerrarArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

int32_t ASLPC_readdir(const char* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_readdir.", NIVEL_LOG_BAJO);

	//TODO CONSULTAR A LA CACHE

	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argLeerDir_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	arg.offset = offset;
	//arg.fInfo = fi;
	paqueteEnvio.tipo = TIPO_LEER_DIRECTORIO;
	paqueteEnvio.pLength = tamanioEnvioLeerDirectorio(arg);
	paqueteEnvio.payload = desparsearEnvioLeerDirectorio(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		reciboConBuffer_t reciboConBuffer = parsearReciboConBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);

		//RECIBO LOS NOMBRES DE LOS ARCHIVOS O DIRECTORIOS CONTENIDOS SEPARADOS POR '\0'
		uint16_t k, i, f;
		for(k = i = f = 0; k < paqueteRecibo.pLength;){
			for(i = f; f < paqueteRecibo.pLength && paqueteRecibo.payload[f] != '\0'; f++);
			//TODO REVISAR
			char* bufferAux = (char*) malloc(f - i + 1);
			if(paqueteRecibo.payload[f] == '\0'){
				memcpy(bufferAux, paqueteRecibo.payload + i, f - i + 1);
			}else{
				memset(bufferAux, '\0', f - i + 1);
				memcpy(bufferAux, paqueteRecibo.payload + i, f - i);
			}
			filler(buf, bufferAux, NULL, 0);
			free(bufferAux);
		}
		free(reciboConBuffer.buffer);
		res = reciboConBuffer.resultado;
	} else {
		res = -ENOENT;
	}

	free(recibo);

	return res;
}

/*int32_t ASLPC_access(const char* path, int32_t mask){
 return 0;
 }*/

int32_t ASLPC_create(const char* path, mode_t modo, struct fuse_file_info* fInfo) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_create.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argCrearArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	arg.modo = modo;
	//arg.fInfo = fInfo;
	paqueteEnvio.tipo = TIPO_CREAR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioCrearArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioCrearArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}
	free(recibo);

	return res;
}

int32_t ASLPC_unlink(const char* path) {
	loguearDatos("DEBUG", time(NULL), "FSClient", 0, 0, "Se ha llamado a la funcion ASLPC_unlink.", NIVEL_LOG_BAJO);
	//ARMO EL PAQUETE A ENVIAR
	paquete_t paqueteEnvio;
	argBorrarArch_t arg;
	arg.path = (char*) malloc(strlen(path) + 1);
	memcpy(arg.path, path, strlen(path) + 1);
	paqueteEnvio.tipo = TIPO_BORRAR_ARCHIVO;
	paqueteEnvio.pLength = tamanioEnvioBorrarArchivo(arg);
	paqueteEnvio.payload = desparsearEnvioBorrarArchivo(arg);
	char* envio = serializar(paqueteEnvio);
	free(arg.path);

	char* recibo;

	//HAGO EL PEDIDO
	uint8_t error = unPedidoUnRecibo(envio, &recibo);
	free(envio);
	free(paqueteEnvio.payload);

	int32_t res;
	if (error != 0) {
		paquete_t paqueteRecibo = deserializar(recibo);
		res = parsearReciboSinBuffer(paqueteRecibo.payload);
		free(paqueteRecibo.payload);
	} else {
		res = -ENOENT;
	}
	free(recibo);

	return res;
}
